library(MSnbase)
Loading required package: BiocGenerics
Loading required package: parallel

Attaching package: ‘BiocGenerics’

The following objects are masked from ‘package:parallel’:

    clusterApply, clusterApplyLB, clusterCall, clusterEvalQ, clusterExport, clusterMap, parApply,
    parCapply, parLapply, parLapplyLB, parRapply, parSapply, parSapplyLB

The following objects are masked from ‘package:stats’:

    IQR, mad, sd, var, xtabs

The following objects are masked from ‘package:base’:

    anyDuplicated, append, as.data.frame, basename, cbind, colnames, dirname, do.call, duplicated,
    eval, evalq, Filter, Find, get, grep, grepl, intersect, is.unsorted, lapply, Map, mapply, match,
    mget, order, paste, pmax, pmax.int, pmin, pmin.int, Position, rank, rbind, Reduce, rownames,
    sapply, setdiff, sort, table, tapply, union, unique, unsplit, which, which.max, which.min

Loading required package: Biobase
Welcome to Bioconductor

    Vignettes contain introductory material; view with 'browseVignettes()'. To cite Bioconductor,
    see 'citation("Biobase")', and for packages 'citation("pkgname")'.

Loading required package: mzR
Loading required package: Rcpp
Loading required package: S4Vectors
Loading required package: stats4

Attaching package: ‘S4Vectors’

The following object is masked from ‘package:base’:

    expand.grid

Loading required package: ProtGenerics

Attaching package: ‘ProtGenerics’

The following object is masked from ‘package:stats’:

    smooth


This is MSnbase version 2.14.2 
  Visit https://lgatto.github.io/MSnbase/ to get started.


Attaching package: ‘MSnbase’

The following object is masked from ‘package:base’:

    trimws
library(pRoloc)
Loading required package: MLInterfaces
Loading required package: annotate
Loading required package: AnnotationDbi
Loading required package: IRanges
Loading required package: XML

Attaching package: ‘annotate’

The following object is masked from ‘package:mzR’:

    nChrom

Loading required package: cluster
Loading required package: BiocParallel
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     

This is pRoloc version 1.28.0 
  Visit https://lgatto.github.io/pRoloc/ to get started.
library(pRolocExt)
library(camprotR)
library(tidyverse)
── Attaching packages ──────────────────────────────────────────────────────────────────────── tidyverse 1.3.0 ──
✓ ggplot2 3.3.2     ✓ purrr   0.3.4
✓ tibble  3.0.3     ✓ dplyr   1.0.4
✓ tidyr   1.1.2     ✓ stringr 1.4.0
✓ readr   1.3.1     ✓ forcats 0.5.0
── Conflicts ─────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::collapse()   masks IRanges::collapse()
x dplyr::combine()    masks MSnbase::combine(), Biobase::combine(), BiocGenerics::combine()
x dplyr::desc()       masks IRanges::desc()
x tidyr::expand()     masks S4Vectors::expand()
x dplyr::filter()     masks stats::filter()
x dplyr::first()      masks S4Vectors::first()
x dplyr::lag()        masks stats::lag()
x ggplot2::Position() masks BiocGenerics::Position(), base::Position()
x purrr::reduce()     masks IRanges::reduce(), MSnbase::reduce()
x dplyr::rename()     masks S4Vectors::rename()
x dplyr::select()     masks AnnotationDbi::select()
x dplyr::slice()      masks IRanges::slice()
source('../plot_foi.R')
Loading required package: broom
Registered S3 methods overwritten by 'biobroom':
  method      from 
  glance.list broom
  tidy.list   broom

Read in the bandle results (pe=posterior estimate) and protein quantification

combined_pe <- readRDS('../../out/combined_pe.rds')
combined_protein_res <- readRDS('../../out/combined_protein_res_for_bandle.rds')

Summarise the bandle results across the replicates to describe the localisations identified in each condition

loc_assignments <- combined_pe %>%
    group_by(protein) %>%
    summarise(bandle.allocation.dmso.n=length(setdiff(unique(bandle.allocation.inc.undefined_DMSO), 'Undefined')),
              bandle.allocation.tg.n=length(setdiff(unique(bandle.allocation.inc.undefined_Tg), 'Undefined')),
              bandle.allocation.dmso.n.obs=length(
                bandle.allocation.inc.undefined_DMSO[bandle.allocation.inc.undefined_DMSO!='Undefined']),
              bandle.allocation.tg.n.obs=length(
                bandle.allocation.inc.undefined_Tg[bandle.allocation.inc.undefined_Tg!='Undefined']),
              bandle.allocation.dmso=paste(bandle.allocation.inc.undefined_DMSO, collapse=','),
              bandle.allocation.tg=paste(bandle.allocation.inc.undefined_Tg, collapse=',')) %>%
    rowwise() %>%
    mutate(bandle.allocation.dmso.minimal=ifelse(
      (bandle.allocation.dmso=='Undefined' | bandle.allocation.dmso.n!=1), 'Undefined',
      setdiff(unlist(strsplit(bandle.allocation.dmso, split=',')), 'Undefined')),
      bandle.allocation.tg.minimal=ifelse(
      (bandle.allocation.tg=='Undefined' | bandle.allocation.tg.n!=1), 'Undefined',
      setdiff(unlist(strsplit(bandle.allocation.tg, split=',')), 'Undefined')))



head(loc_assignments)

loc_assignments %>% filter(bandle.allocation.dmso.n==3)

table(loc_assignments$bandle.allocation.dmso.minimal)

        CYTOSOL              ER           GOLGI        LYSOSOME    MITOCHONDRIA   NUCLEOPLASM-1   NUCLEOPLASM-2 
            563             145             124              80             244             168             463 
        NUCLEUS      PEROXISOME              PM PROTEIN COMPLEX        RIBOSOME       Undefined 
            297              32             141             411             109            1855 
table(loc_assignments$bandle.allocation.tg.minimal)

        CYTOSOL              ER           GOLGI        LYSOSOME    MITOCHONDRIA   NUCLEOPLASM-1   NUCLEOPLASM-2 
            557             189              76             106             316             164             580 
        NUCLEUS      PEROXISOME              PM PROTEIN COMPLEX        RIBOSOME       Undefined 
            299              46              93             415              83            1708 
consistent_loc <- loc_assignments %>% filter(bandle.allocation.dmso.n<=1, bandle.allocation.tg.n<=1)

Add the bandle localisation assignments to the protein quantification object.

loc_assignments_per_condition <- NULL
loc_assignments_per_condition$DMSO <- loc_assignments %>%
  select(bandle_alloc=bandle.allocation.dmso.minimal, bandle_alloc_all=bandle.allocation.dmso, protein)
loc_assignments_per_condition$Thapsigargin <- loc_assignments %>%
  select(bandle_alloc=bandle.allocation.tg.minimal, bandle_alloc_all=bandle.allocation.tg, protein)

combined_protein_res_inc_bandle_loc <- combined_protein_res %>% names() %>% lapply(function(condition){
  x <- combined_protein_res[[condition]]
  new_feature_data <- merge(fData(x), loc_assignments_per_condition[[condition]], by.x='row.names', by.y='protein', all.x=TRUE) %>%
    tibble::column_to_rownames('Row.names')
  
  fData(x) <- new_feature_data[rownames(x),]
  return(x)
})

names(combined_protein_res_inc_bandle_loc) <- names(combined_protein_res)

table(fData(combined_protein_res_inc_bandle_loc$DMSO)$bandle_alloc)

        CYTOSOL              ER           GOLGI        LYSOSOME    MITOCHONDRIA   NUCLEOPLASM-1   NUCLEOPLASM-2 
            563             145             124              80             244             168             463 
        NUCLEUS      PEROXISOME              PM PROTEIN COMPLEX        RIBOSOME       Undefined 
            297              32             141             411             109            1855 
table(fData(combined_protein_res_inc_bandle_loc$Thapsigargin)$bandle_alloc)

        CYTOSOL              ER           GOLGI        LYSOSOME    MITOCHONDRIA   NUCLEOPLASM-1   NUCLEOPLASM-2 
            557             189              76             106             316             164             580 
        NUCLEUS      PEROXISOME              PM PROTEIN COMPLEX        RIBOSOME       Undefined 
            299              46              93             415              83            1708 

Define a function to obtain the differential localisations

get_diff_loc <- function(threshold, name, min_rep=2){
  
  combined_pe %>%
    filter(bandle.differential.localisation>threshold) %>%
    group_by(protein) %>%
    summarise(n.diff.loc.rep=length(replicate),
              diff.loc.reps=paste(replicate, collapse=',')) %>%
    merge(loc_assignments, by='protein') %>%
    rowwise() %>%
    filter(n.diff.loc.rep>=min_rep,
           length(intersect(setdiff(unlist(strsplit(bandle.allocation.dmso, split=',')), 'Undefined'),
                            setdiff(unlist(strsplit(bandle.allocation.tg, split=',')), 'Undefined')))==0) %>%
    mutate(level=name) %>%
    merge(fData(combined_protein_res$DMSO)[,174:177], by.x='protein', by.y='row.names')
  
}

Subset the bandle results by 3 threshold on the differential localisation probability and determine the relocalising proteins with each threshold, then combine results into a single data.frame.

diff_loc_high_conf <- get_diff_loc(0.99, 'Highly confident')
diff_loc_conf <- get_diff_loc(0.95, 'Confident')
diff_loc_cand <- get_diff_loc(0.85, 'Candidate')

diff_loc_all <- bind_rows(diff_loc_high_conf, diff_loc_conf, diff_loc_cand) %>%
  mutate(level=factor(level, levels=c('Highly confident', 'Confident', 'Candidate')))

diff_loc_all_unique <- diff_loc_all %>%
  group_by(protein) %>%
  slice_min(order_by=level, n=1) %>%
  ungroup()

table(diff_loc_all$level)
table(diff_loc_all$level, diff_loc_all$diff.loc.reps)

table(diff_loc_all_unique$level)
table(diff_loc_all_unique$level, diff_loc_all_unique$diff.loc.reps)

Save for downstream notebooks

saveRDS(loc_assignments, '../../out/bandle_loc_assignments.rds')
saveRDS(diff_loc_all, '../../out/bandle_diff_loc_all.rds')
saveRDS(diff_loc_all_unique, '../../out/bandle_diff_loc_all_unique.rds')
saveRDS(combined_protein_res_inc_bandle_loc, '../../out/combined_protein_res_inc_bandle_loc.rds')

Define a function to plot the differential localisation as a tile plot






plot_diff_loc_tile <- function(obj){
  obj %>%
    group_by(DMSO=update_loc_names(bandle.allocation.dmso.minimal),
             Tg=update_loc_names(bandle.allocation.tg.minimal)) %>%
    tally() %>%
    ggplot(aes(DMSO, Tg, fill=n)) +
    geom_tile() +
    theme_camprot(base_size=15, base_family='sans') +
    theme(axis.text.x=element_text(angle=45, vjust=1, hjust=1)) +
    scale_fill_continuous(low='grey90', high=get_cat_palette(6)[6], guide=FALSE) +
    xlab('DMSO') +
    ylab('Tg') +
    geom_text(aes(label=n))
}

Plot localisations for all localisation assignments and for each level of confidence of relocalisation

plot_diff_loc_tile(loc_assignments)

diff_loc_all_unique %>% filter(level=='Highly confident') %>% plot_diff_loc_tile()


p <- diff_loc_all_unique %>% filter(level %in% c('Highly confident', 'Confident')) %>% plot_diff_loc_tile()
print(p)
ggsave('../../../../5_manuscript_figures/Figure_4/reloc/tile.png', width=4, height=4)
ggsave('../../../../5_manuscript_figures/Figure_4/reloc/tile.pdf', width=4, height=4)

diff_loc_all_unique %>% plot_diff_loc_tile()

Aluvial plots for relocalisation

library(ggalluvial)

colours <- readRDS('../../../../6_shiny_app/out/shiny_colours.rds')$Protein

colours <- c(colours[getMarkerClasses(combined_protein_res$DMSO)], 'grey85') %>% unname()
marker_levels=update_loc_names(c(getMarkerClasses(combined_protein_res$DMSO), 'Undefined'))


plot_alluvial <- function(obj, remove_same=TRUE){
  for_alluvial <- obj %>%
    mutate(DMSO=update_loc_names(bandle.allocation.dmso.minimal),
           Tg=update_loc_names(bandle.allocation.tg.minimal)) %>%
    dplyr::select(DMSO, Tg)
  
  if(remove_same){
    for_alluvial <- for_alluvial %>%
    filter(DMSO!=Tg)
  }
  
  for_alluvial <- for_alluvial %>%
    to_lodes() %>%
    mutate(stratum=factor(stratum, levels=marker_levels))
  
  
  for_alluvial %>%
    ggplot(aes(x, stratum=stratum, alluvium=alluvium, fill=stratum, label = stratum)) +
    geom_alluvium(width=1/8) +
    geom_stratum(width=1/8) +
    theme_camprot(base_size=15, base_family='sans') +
    theme(aspect.ratio=1.5, 
          axis.text.y=element_blank(), 
          axis.ticks=element_blank(),
          axis.title=element_blank(),
          axis.line=element_blank(),
          panel.border=element_blank()) +
    #geom_text(stat = "stratum", size = 3, data=x[x$x=='DMSO',], hjust=1) +
    #geom_text_repel(stat = "stratum", size = 3, data=x[x$x=='DMSO',], min.segment.length=3, hjust=1) +
    scale_fill_manual(values=colours[marker_levels %in% unique(for_alluvial$stratum)], name='')
  
}

plot_alluvial(loc_assignments, remove_same=FALSE)
diff_loc_all_unique %>% filter(level=='Highly confident') %>% plot_alluvial()

p <- diff_loc_all_unique %>% filter(level %in% c('Highly confident', 'Confident')) %>% plot_alluvial()
print(p)
ggsave('../../../../5_manuscript_figures/Figure_4/reloc/alluvial.png', width=5, height=5)
ggsave('../../../../5_manuscript_figures/Figure_4/reloc/alluvial.pdf', width=5, height=5)

diff_loc_all_unique %>% plot_alluvial()

In the next few cells, we plot specific subsets of protein relocalisations to help with the interpretation.

Ribo2Un <- diff_loc_all_unique %>%
  filter(bandle.allocation.dmso.minimal=='RIBOSOME', bandle.allocation.tg.minimal=='Undefined', level!='Candidate')

print(Ribo2Un)

plot_fois(Ribo2Un$protein,
          foi_name='Ribosome -> Undefined',
          moi=c('RIBOSOME', 'ER', 'PROTEIN COMPLEX', 'CYTOSOL'),
          obj=combined_protein_res_inc_bandle_loc,
          feature_col='bandle_alloc',
          plot_tsne=TRUE,
          unknown_desc='Undefined')


for(x in Ribo2Un$protein){
  plot_fois(x,
          foi_name=x,
          moi=c('RIBOSOME', 'ER', 'PROTEIN COMPLEX', 'NUCLEUS'),
          obj=combined_protein_res_inc_bandle_loc,
          feature_col='bandle_alloc',
          unknown_desc='Undefined')
}

`fun.y` is deprecated. Use `fun` instead.

`fun.y` is deprecated. Use `fun` instead.

`fun.y` is deprecated. Use `fun` instead.

`fun.y` is deprecated. Use `fun` instead.

`fun.y` is deprecated. Use `fun` instead.

`fun.y` is deprecated. Use `fun` instead.


Ribo2Any <- diff_loc_all_unique %>%
  filter(bandle.allocation.dmso.minimal=='RIBOSOME')

print(Ribo2Any %>% arrange(level))

plot_fois(Ribo2Any$protein,
          foi_name='Away from ribosome',
          moi=c('RIBOSOME', 'ER', 'PROTEIN COMPLEX', 'CYTOSOL'),
          obj=combined_protein_res_inc_bandle_loc,
          feature_col='bandle_alloc',
          plot_tsne=TRUE,
          unknown_desc='Undefined')

for(x in Ribo2Any$protein){
  plot_fois(x,
          foi_name=x,
          moi=c('RIBOSOME', 'ER', 'PROTEIN COMPLEX', 'NUCLEUS'),
          obj=combined_protein_res,
          feature_col='markers')
}
PM2ER <- diff_loc_all_unique %>%
  filter(bandle.allocation.dmso.minimal=='PM',
         bandle.allocation.tg.minimal=='ER')

print(PM2ER)

plot_fois(PM2ER$protein,
          foi_name='PM->ER',
          moi=c('PM', 'ER', 'LYSOSOME', 'MITOCHONDRIA', 'PEROXISOME'),
          obj=combined_protein_res_inc_bandle_loc,
          feature_col='bandle_alloc',
          plot_tsne=TRUE,
          unknown_desc='Undefined')

NA
NA
Any2Nuc <- diff_loc_all_unique %>%
  filter(grepl('NUC', bandle.allocation.tg.minimal))

print(Any2Nuc %>% arrange(level))


plot_fois(Any2Nuc$protein, foi_name='To Nucleoplasm',
          moi=c('CYTOSOL', 'NUCLEUS', 'NUCLEOPLASM-1', 'NUCLEOPLASM-2', 'ER'),
          obj=combined_protein_res,
          feature_col='markers')

Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing
scale.

for(x in Any2Nuc$protein){
  plot_fois(x, foi_name=x,
          moi=c('CYTOSOL', 'NUCLEUS', 'NUCLEOPLASM-1', 'NUCLEOPLASM-2', 'ER'),
          obj=combined_protein_res,
          feature_col='markers')
}

`fun.y` is deprecated. Use `fun` instead.

`fun.y` is deprecated. Use `fun` instead.

`fun.y` is deprecated. Use `fun` instead.

`fun.y` is deprecated. Use `fun` instead.

`fun.y` is deprecated. Use `fun` instead.

`fun.y` is deprecated. Use `fun` instead.

`fun.y` is deprecated. Use `fun` instead.

`fun.y` is deprecated. Use `fun` instead.

`fun.y` is deprecated. Use `fun` instead.

`fun.y` is deprecated. Use `fun` instead.

ga2any <- diff_loc_conf %>%
  filter(bandle.allocation.dmso.minimal=='GOLGI')

print(ga2any %>% arrange(level))

plot_fois(ga2any$protein,
          foi_name='Away from golgi',
          moi=c('GOLGI', 'ER', 'LYSOSOME', 'CYTOSOL', 'NUCLEUS'),
          obj=combined_protein_res,
          feature_col='markers',
          plot_tsne=TRUE)

plot_fois('Q92688',
          foi_name='Q92688',
          moi=c('GOLGI', 'ER', 'LYSOSOME', 'CYTOSOL', 'NUCLEUS'),
          obj=combined_protein_res_inc_bandle_loc,
          feature_col='bandle_alloc',
          plot_tsne=TRUE,
          unknown_desc='Undefined')

NA
NA
un2un <- diff_loc_all_unique %>%
  filter(bandle.allocation.dmso.minimal=='Undefined',
         bandle.allocation.tg.minimal=='Undefined',
         diff.loc.reps=='1,2,3')

print(un2un)

plot_fois(un2un$protein,
          foi_name='?->?',
          moi=c('GOLGI', 'ER', 'LYSOSOME', 'CYTOSOL', 'NUCLEUS'),
          obj=combined_protein_res,
          feature_col='markers',
          plot_tsne=TRUE)

LS0tCnRpdGxlOiAiRXhhbWluaW5nIGRpZmZlcmVudGlhbCBwcm90ZWluIGxvY2FsaXNhdGlvbiByZXN1bHRzIgphdXRob3I6CiAgLSBuYW1lOiAiVG9tIFNtaXRoIgogICAgYWZmaWxpYXRpb246ICJDYW1icmlkZ2UgQ2VudHJlIGZvciBQcm90ZW9taWNzIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiCmFic3RyYWN0OiB8IAogIEhlcmUsIHdlIGV4YW1pbmUgdGhlIEJBTkRMRSByZXN1bHRzIGFuZCBpZGVudGlmeSBjb25zaXN0ZW50IGNoYW5nZXMgaW4gbG9jYWxpc2F0aW9uIGFjcm9zcyB0aGUgcmVwbGljYXRlcwpvdXRwdXQ6CiAgcGRmX2RvY3VtZW50OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKZ2VvbWV0cnk6IG1hcmdpbj0xaW4KZm9udHNpemU6IDExcHQKLS0tCgpgYGB7cn0KCmxpYnJhcnkoTVNuYmFzZSkKbGlicmFyeShwUm9sb2MpCmxpYnJhcnkocFJvbG9jRXh0KQpsaWJyYXJ5KGNhbXByb3RSKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCgpzb3VyY2UoJy4uL3Bsb3RfZm9pLlInKQpgYGAKClJlYWQgaW4gdGhlIGJhbmRsZSByZXN1bHRzIChwZT1wb3N0ZXJpb3IgZXN0aW1hdGUpIGFuZCBwcm90ZWluIHF1YW50aWZpY2F0aW9uCmBgYHtyfQpjb21iaW5lZF9wZSA8LSByZWFkUkRTKCcuLi8uLi9vdXQvY29tYmluZWRfcGUucmRzJykKY29tYmluZWRfcHJvdGVpbl9yZXMgPC0gcmVhZFJEUygnLi4vLi4vb3V0L2NvbWJpbmVkX3Byb3RlaW5fcmVzX2Zvcl9iYW5kbGUucmRzJykKYGBgCgpTdW1tYXJpc2UgdGhlIGJhbmRsZSByZXN1bHRzIGFjcm9zcyB0aGUgcmVwbGljYXRlcyB0byBkZXNjcmliZSB0aGUgbG9jYWxpc2F0aW9ucyBpZGVudGlmaWVkIGluIGVhY2ggY29uZGl0aW9uCmBgYHtyfQpsb2NfYXNzaWdubWVudHMgPC0gY29tYmluZWRfcGUgJT4lCiAgICBncm91cF9ieShwcm90ZWluKSAlPiUKICAgIHN1bW1hcmlzZShiYW5kbGUuYWxsb2NhdGlvbi5kbXNvLm49bGVuZ3RoKHNldGRpZmYodW5pcXVlKGJhbmRsZS5hbGxvY2F0aW9uLmluYy51bmRlZmluZWRfRE1TTyksICdVbmRlZmluZWQnKSksCiAgICAgICAgICAgICAgYmFuZGxlLmFsbG9jYXRpb24udGcubj1sZW5ndGgoc2V0ZGlmZih1bmlxdWUoYmFuZGxlLmFsbG9jYXRpb24uaW5jLnVuZGVmaW5lZF9UZyksICdVbmRlZmluZWQnKSksCiAgICAgICAgICAgICAgYmFuZGxlLmFsbG9jYXRpb24uZG1zby5uLm9icz1sZW5ndGgoCiAgICAgICAgICAgICAgICBiYW5kbGUuYWxsb2NhdGlvbi5pbmMudW5kZWZpbmVkX0RNU09bYmFuZGxlLmFsbG9jYXRpb24uaW5jLnVuZGVmaW5lZF9ETVNPIT0nVW5kZWZpbmVkJ10pLAogICAgICAgICAgICAgIGJhbmRsZS5hbGxvY2F0aW9uLnRnLm4ub2JzPWxlbmd0aCgKICAgICAgICAgICAgICAgIGJhbmRsZS5hbGxvY2F0aW9uLmluYy51bmRlZmluZWRfVGdbYmFuZGxlLmFsbG9jYXRpb24uaW5jLnVuZGVmaW5lZF9UZyE9J1VuZGVmaW5lZCddKSwKICAgICAgICAgICAgICBiYW5kbGUuYWxsb2NhdGlvbi5kbXNvPXBhc3RlKGJhbmRsZS5hbGxvY2F0aW9uLmluYy51bmRlZmluZWRfRE1TTywgY29sbGFwc2U9JywnKSwKICAgICAgICAgICAgICBiYW5kbGUuYWxsb2NhdGlvbi50Zz1wYXN0ZShiYW5kbGUuYWxsb2NhdGlvbi5pbmMudW5kZWZpbmVkX1RnLCBjb2xsYXBzZT0nLCcpKSAlPiUKICAgIHJvd3dpc2UoKSAlPiUKICAgIG11dGF0ZShiYW5kbGUuYWxsb2NhdGlvbi5kbXNvLm1pbmltYWw9aWZlbHNlKAogICAgICAoYmFuZGxlLmFsbG9jYXRpb24uZG1zbz09J1VuZGVmaW5lZCcgfCBiYW5kbGUuYWxsb2NhdGlvbi5kbXNvLm4hPTEpLCAnVW5kZWZpbmVkJywKICAgICAgc2V0ZGlmZih1bmxpc3Qoc3Ryc3BsaXQoYmFuZGxlLmFsbG9jYXRpb24uZG1zbywgc3BsaXQ9JywnKSksICdVbmRlZmluZWQnKSksCiAgICAgIGJhbmRsZS5hbGxvY2F0aW9uLnRnLm1pbmltYWw9aWZlbHNlKAogICAgICAoYmFuZGxlLmFsbG9jYXRpb24udGc9PSdVbmRlZmluZWQnIHwgYmFuZGxlLmFsbG9jYXRpb24udGcubiE9MSksICdVbmRlZmluZWQnLAogICAgICBzZXRkaWZmKHVubGlzdChzdHJzcGxpdChiYW5kbGUuYWxsb2NhdGlvbi50Zywgc3BsaXQ9JywnKSksICdVbmRlZmluZWQnKSkpCgoKCmhlYWQobG9jX2Fzc2lnbm1lbnRzKQoKbG9jX2Fzc2lnbm1lbnRzICU+JSBmaWx0ZXIoYmFuZGxlLmFsbG9jYXRpb24uZG1zby5uPT0zKQoKdGFibGUobG9jX2Fzc2lnbm1lbnRzJGJhbmRsZS5hbGxvY2F0aW9uLmRtc28ubWluaW1hbCkKdGFibGUobG9jX2Fzc2lnbm1lbnRzJGJhbmRsZS5hbGxvY2F0aW9uLnRnLm1pbmltYWwpCgpjb25zaXN0ZW50X2xvYyA8LSBsb2NfYXNzaWdubWVudHMgJT4lIGZpbHRlcihiYW5kbGUuYWxsb2NhdGlvbi5kbXNvLm48PTEsIGJhbmRsZS5hbGxvY2F0aW9uLnRnLm48PTEpCgpgYGAKCgpBZGQgdGhlIGJhbmRsZSBsb2NhbGlzYXRpb24gYXNzaWdubWVudHMgdG8gdGhlIHByb3RlaW4gcXVhbnRpZmljYXRpb24gb2JqZWN0LgpgYGB7cn0KbG9jX2Fzc2lnbm1lbnRzX3Blcl9jb25kaXRpb24gPC0gTlVMTApsb2NfYXNzaWdubWVudHNfcGVyX2NvbmRpdGlvbiRETVNPIDwtIGxvY19hc3NpZ25tZW50cyAlPiUKICBzZWxlY3QoYmFuZGxlX2FsbG9jPWJhbmRsZS5hbGxvY2F0aW9uLmRtc28ubWluaW1hbCwgYmFuZGxlX2FsbG9jX2FsbD1iYW5kbGUuYWxsb2NhdGlvbi5kbXNvLCBwcm90ZWluKQpsb2NfYXNzaWdubWVudHNfcGVyX2NvbmRpdGlvbiRUaGFwc2lnYXJnaW4gPC0gbG9jX2Fzc2lnbm1lbnRzICU+JQogIHNlbGVjdChiYW5kbGVfYWxsb2M9YmFuZGxlLmFsbG9jYXRpb24udGcubWluaW1hbCwgYmFuZGxlX2FsbG9jX2FsbD1iYW5kbGUuYWxsb2NhdGlvbi50ZywgcHJvdGVpbikKCmNvbWJpbmVkX3Byb3RlaW5fcmVzX2luY19iYW5kbGVfbG9jIDwtIGNvbWJpbmVkX3Byb3RlaW5fcmVzICU+JSBuYW1lcygpICU+JSBsYXBwbHkoZnVuY3Rpb24oY29uZGl0aW9uKXsKICB4IDwtIGNvbWJpbmVkX3Byb3RlaW5fcmVzW1tjb25kaXRpb25dXQogIG5ld19mZWF0dXJlX2RhdGEgPC0gbWVyZ2UoZkRhdGEoeCksIGxvY19hc3NpZ25tZW50c19wZXJfY29uZGl0aW9uW1tjb25kaXRpb25dXSwgYnkueD0ncm93Lm5hbWVzJywgYnkueT0ncHJvdGVpbicsIGFsbC54PVRSVUUpICU+JQogICAgdGliYmxlOjpjb2x1bW5fdG9fcm93bmFtZXMoJ1Jvdy5uYW1lcycpCiAgCiAgZkRhdGEoeCkgPC0gbmV3X2ZlYXR1cmVfZGF0YVtyb3duYW1lcyh4KSxdCiAgcmV0dXJuKHgpCn0pCgpuYW1lcyhjb21iaW5lZF9wcm90ZWluX3Jlc19pbmNfYmFuZGxlX2xvYykgPC0gbmFtZXMoY29tYmluZWRfcHJvdGVpbl9yZXMpCgp0YWJsZShmRGF0YShjb21iaW5lZF9wcm90ZWluX3Jlc19pbmNfYmFuZGxlX2xvYyRETVNPKSRiYW5kbGVfYWxsb2MpCnRhYmxlKGZEYXRhKGNvbWJpbmVkX3Byb3RlaW5fcmVzX2luY19iYW5kbGVfbG9jJFRoYXBzaWdhcmdpbikkYmFuZGxlX2FsbG9jKQpgYGAKCkRlZmluZSBhIGZ1bmN0aW9uIHRvIG9idGFpbiB0aGUgZGlmZmVyZW50aWFsIGxvY2FsaXNhdGlvbnMKYGBge3J9CmdldF9kaWZmX2xvYyA8LSBmdW5jdGlvbih0aHJlc2hvbGQsIG5hbWUsIG1pbl9yZXA9Mil7CiAgCiAgY29tYmluZWRfcGUgJT4lCiAgICBmaWx0ZXIoYmFuZGxlLmRpZmZlcmVudGlhbC5sb2NhbGlzYXRpb24+dGhyZXNob2xkKSAlPiUKICAgIGdyb3VwX2J5KHByb3RlaW4pICU+JQogICAgc3VtbWFyaXNlKG4uZGlmZi5sb2MucmVwPWxlbmd0aChyZXBsaWNhdGUpLAogICAgICAgICAgICAgIGRpZmYubG9jLnJlcHM9cGFzdGUocmVwbGljYXRlLCBjb2xsYXBzZT0nLCcpKSAlPiUKICAgIG1lcmdlKGxvY19hc3NpZ25tZW50cywgYnk9J3Byb3RlaW4nKSAlPiUKICAgIHJvd3dpc2UoKSAlPiUKICAgIGZpbHRlcihuLmRpZmYubG9jLnJlcD49bWluX3JlcCwKICAgICAgICAgICBsZW5ndGgoaW50ZXJzZWN0KHNldGRpZmYodW5saXN0KHN0cnNwbGl0KGJhbmRsZS5hbGxvY2F0aW9uLmRtc28sIHNwbGl0PScsJykpLCAnVW5kZWZpbmVkJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXRkaWZmKHVubGlzdChzdHJzcGxpdChiYW5kbGUuYWxsb2NhdGlvbi50Zywgc3BsaXQ9JywnKSksICdVbmRlZmluZWQnKSkpPT0wKSAlPiUKICAgIG11dGF0ZShsZXZlbD1uYW1lKSAlPiUKICAgIG1lcmdlKGZEYXRhKGNvbWJpbmVkX3Byb3RlaW5fcmVzJERNU08pWywxNzQ6MTc3XSwgYnkueD0ncHJvdGVpbicsIGJ5Lnk9J3Jvdy5uYW1lcycpCiAgCn0KCmBgYAoKU3Vic2V0IHRoZSBiYW5kbGUgcmVzdWx0cyBieSAzIHRocmVzaG9sZCBvbiB0aGUgZGlmZmVyZW50aWFsIGxvY2FsaXNhdGlvbiBwcm9iYWJpbGl0eSBhbmQgZGV0ZXJtaW5lIHRoZSByZWxvY2FsaXNpbmcgcHJvdGVpbnMgd2l0aCBlYWNoIHRocmVzaG9sZCwgdGhlbiBjb21iaW5lIHJlc3VsdHMgaW50byBhIHNpbmdsZSBkYXRhLmZyYW1lLgpgYGB7cn0KZGlmZl9sb2NfaGlnaF9jb25mIDwtIGdldF9kaWZmX2xvYygwLjk5LCAnSGlnaGx5IGNvbmZpZGVudCcpCmRpZmZfbG9jX2NvbmYgPC0gZ2V0X2RpZmZfbG9jKDAuOTUsICdDb25maWRlbnQnKQpkaWZmX2xvY19jYW5kIDwtIGdldF9kaWZmX2xvYygwLjg1LCAnQ2FuZGlkYXRlJykKCmRpZmZfbG9jX2FsbCA8LSBiaW5kX3Jvd3MoZGlmZl9sb2NfaGlnaF9jb25mLCBkaWZmX2xvY19jb25mLCBkaWZmX2xvY19jYW5kKSAlPiUKICBtdXRhdGUobGV2ZWw9ZmFjdG9yKGxldmVsLCBsZXZlbHM9YygnSGlnaGx5IGNvbmZpZGVudCcsICdDb25maWRlbnQnLCAnQ2FuZGlkYXRlJykpKQoKZGlmZl9sb2NfYWxsX3VuaXF1ZSA8LSBkaWZmX2xvY19hbGwgJT4lCiAgZ3JvdXBfYnkocHJvdGVpbikgJT4lCiAgc2xpY2VfbWluKG9yZGVyX2J5PWxldmVsLCBuPTEpICU+JQogIHVuZ3JvdXAoKQoKdGFibGUoZGlmZl9sb2NfYWxsJGxldmVsKQp0YWJsZShkaWZmX2xvY19hbGwkbGV2ZWwsIGRpZmZfbG9jX2FsbCRkaWZmLmxvYy5yZXBzKQoKdGFibGUoZGlmZl9sb2NfYWxsX3VuaXF1ZSRsZXZlbCkKdGFibGUoZGlmZl9sb2NfYWxsX3VuaXF1ZSRsZXZlbCwgZGlmZl9sb2NfYWxsX3VuaXF1ZSRkaWZmLmxvYy5yZXBzKQpgYGAKCgpTYXZlIGZvciBkb3duc3RyZWFtIG5vdGVib29rcwpgYGB7cn0Kc2F2ZVJEUyhsb2NfYXNzaWdubWVudHMsICcuLi8uLi9vdXQvYmFuZGxlX2xvY19hc3NpZ25tZW50cy5yZHMnKQpzYXZlUkRTKGRpZmZfbG9jX2FsbCwgJy4uLy4uL291dC9iYW5kbGVfZGlmZl9sb2NfYWxsLnJkcycpCnNhdmVSRFMoZGlmZl9sb2NfYWxsX3VuaXF1ZSwgJy4uLy4uL291dC9iYW5kbGVfZGlmZl9sb2NfYWxsX3VuaXF1ZS5yZHMnKQpzYXZlUkRTKGNvbWJpbmVkX3Byb3RlaW5fcmVzX2luY19iYW5kbGVfbG9jLCAnLi4vLi4vb3V0L2NvbWJpbmVkX3Byb3RlaW5fcmVzX2luY19iYW5kbGVfbG9jLnJkcycpCgpgYGAKCkRlZmluZSBhIGZ1bmN0aW9uIHRvIHBsb3QgdGhlIGRpZmZlcmVudGlhbCBsb2NhbGlzYXRpb24gYXMgYSB0aWxlIHBsb3QKYGBge3J9CgoKCgoKcGxvdF9kaWZmX2xvY190aWxlIDwtIGZ1bmN0aW9uKG9iail7CiAgb2JqICU+JQogICAgZ3JvdXBfYnkoRE1TTz11cGRhdGVfbG9jX25hbWVzKGJhbmRsZS5hbGxvY2F0aW9uLmRtc28ubWluaW1hbCksCiAgICAgICAgICAgICBUZz11cGRhdGVfbG9jX25hbWVzKGJhbmRsZS5hbGxvY2F0aW9uLnRnLm1pbmltYWwpKSAlPiUKICAgIHRhbGx5KCkgJT4lCiAgICBnZ3Bsb3QoYWVzKERNU08sIFRnLCBmaWxsPW4pKSArCiAgICBnZW9tX3RpbGUoKSArCiAgICB0aGVtZV9jYW1wcm90KGJhc2Vfc2l6ZT0xNSwgYmFzZV9mYW1pbHk9J3NhbnMnKSArCiAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIHZqdXN0PTEsIGhqdXN0PTEpKSArCiAgICBzY2FsZV9maWxsX2NvbnRpbnVvdXMobG93PSdncmV5OTAnLCBoaWdoPWdldF9jYXRfcGFsZXR0ZSg2KVs2XSwgZ3VpZGU9RkFMU0UpICsKICAgIHhsYWIoJ0RNU08nKSArCiAgICB5bGFiKCdUZycpICsKICAgIGdlb21fdGV4dChhZXMobGFiZWw9bikpCn0KCmBgYAoKUGxvdCBsb2NhbGlzYXRpb25zIGZvciBhbGwgbG9jYWxpc2F0aW9uIGFzc2lnbm1lbnRzIGFuZCBmb3IgZWFjaCBsZXZlbCBvZiBjb25maWRlbmNlIG9mIHJlbG9jYWxpc2F0aW9uCmBgYHtyfQpwbG90X2RpZmZfbG9jX3RpbGUobG9jX2Fzc2lnbm1lbnRzKQpkaWZmX2xvY19hbGxfdW5pcXVlICU+JSBmaWx0ZXIobGV2ZWw9PSdIaWdobHkgY29uZmlkZW50JykgJT4lIHBsb3RfZGlmZl9sb2NfdGlsZSgpCgpwIDwtIGRpZmZfbG9jX2FsbF91bmlxdWUgJT4lIGZpbHRlcihsZXZlbCAlaW4lIGMoJ0hpZ2hseSBjb25maWRlbnQnLCAnQ29uZmlkZW50JykpICU+JSBwbG90X2RpZmZfbG9jX3RpbGUoKQpwcmludChwKQpnZ3NhdmUoJy4uLy4uLy4uLy4uLzVfbWFudXNjcmlwdF9maWd1cmVzL0ZpZ3VyZV80L3JlbG9jL3RpbGUucG5nJywgd2lkdGg9NCwgaGVpZ2h0PTQpCmdnc2F2ZSgnLi4vLi4vLi4vLi4vNV9tYW51c2NyaXB0X2ZpZ3VyZXMvRmlndXJlXzQvcmVsb2MvdGlsZS5wZGYnLCB3aWR0aD00LCBoZWlnaHQ9NCkKZGlmZl9sb2NfYWxsX3VuaXF1ZSAlPiUgcGxvdF9kaWZmX2xvY190aWxlKCkKYGBgCgpBbHV2aWFsIHBsb3RzIGZvciByZWxvY2FsaXNhdGlvbgoKYGBge3J9CmxpYnJhcnkoZ2dhbGx1dmlhbCkKCmNvbG91cnMgPC0gcmVhZFJEUygnLi4vLi4vLi4vLi4vNl9zaGlueV9hcHAvb3V0L3NoaW55X2NvbG91cnMucmRzJykkUHJvdGVpbgoKY29sb3VycyA8LSBjKGNvbG91cnNbZ2V0TWFya2VyQ2xhc3Nlcyhjb21iaW5lZF9wcm90ZWluX3JlcyRETVNPKV0sICdncmV5ODUnKSAlPiUgdW5uYW1lKCkKbWFya2VyX2xldmVscz11cGRhdGVfbG9jX25hbWVzKGMoZ2V0TWFya2VyQ2xhc3Nlcyhjb21iaW5lZF9wcm90ZWluX3JlcyRETVNPKSwgJ1VuZGVmaW5lZCcpKQoKCnBsb3RfYWxsdXZpYWwgPC0gZnVuY3Rpb24ob2JqLCByZW1vdmVfc2FtZT1UUlVFKXsKICBmb3JfYWxsdXZpYWwgPC0gb2JqICU+JQogICAgbXV0YXRlKERNU089dXBkYXRlX2xvY19uYW1lcyhiYW5kbGUuYWxsb2NhdGlvbi5kbXNvLm1pbmltYWwpLAogICAgICAgICAgIFRnPXVwZGF0ZV9sb2NfbmFtZXMoYmFuZGxlLmFsbG9jYXRpb24udGcubWluaW1hbCkpICU+JQogICAgZHBseXI6OnNlbGVjdChETVNPLCBUZykKICAKICBpZihyZW1vdmVfc2FtZSl7CiAgICBmb3JfYWxsdXZpYWwgPC0gZm9yX2FsbHV2aWFsICU+JQogICAgZmlsdGVyKERNU08hPVRnKQogIH0KICAKICBmb3JfYWxsdXZpYWwgPC0gZm9yX2FsbHV2aWFsICU+JQogICAgdG9fbG9kZXMoKSAlPiUKICAgIG11dGF0ZShzdHJhdHVtPWZhY3RvcihzdHJhdHVtLCBsZXZlbHM9bWFya2VyX2xldmVscykpCiAgCiAgCiAgZm9yX2FsbHV2aWFsICU+JQogICAgZ2dwbG90KGFlcyh4LCBzdHJhdHVtPXN0cmF0dW0sIGFsbHV2aXVtPWFsbHV2aXVtLCBmaWxsPXN0cmF0dW0sIGxhYmVsID0gc3RyYXR1bSkpICsKICAgIGdlb21fYWxsdXZpdW0od2lkdGg9MS84KSArCiAgICBnZW9tX3N0cmF0dW0od2lkdGg9MS84KSArCiAgICB0aGVtZV9jYW1wcm90KGJhc2Vfc2l6ZT0xNSwgYmFzZV9mYW1pbHk9J3NhbnMnKSArCiAgICB0aGVtZShhc3BlY3QucmF0aW89MS41LCAKICAgICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICBheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy5saW5lPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHBhbmVsLmJvcmRlcj1lbGVtZW50X2JsYW5rKCkpICsKICAgICNnZW9tX3RleHQoc3RhdCA9ICJzdHJhdHVtIiwgc2l6ZSA9IDMsIGRhdGE9eFt4JHg9PSdETVNPJyxdLCBoanVzdD0xKSArCiAgICAjZ2VvbV90ZXh0X3JlcGVsKHN0YXQgPSAic3RyYXR1bSIsIHNpemUgPSAzLCBkYXRhPXhbeCR4PT0nRE1TTycsXSwgbWluLnNlZ21lbnQubGVuZ3RoPTMsIGhqdXN0PTEpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jb2xvdXJzW21hcmtlcl9sZXZlbHMgJWluJSB1bmlxdWUoZm9yX2FsbHV2aWFsJHN0cmF0dW0pXSwgbmFtZT0nJykKICAKfQoKcGxvdF9hbGx1dmlhbChsb2NfYXNzaWdubWVudHMsIHJlbW92ZV9zYW1lPUZBTFNFKQpkaWZmX2xvY19hbGxfdW5pcXVlICU+JSBmaWx0ZXIobGV2ZWw9PSdIaWdobHkgY29uZmlkZW50JykgJT4lIHBsb3RfYWxsdXZpYWwoKQoKcCA8LSBkaWZmX2xvY19hbGxfdW5pcXVlICU+JSBmaWx0ZXIobGV2ZWwgJWluJSBjKCdIaWdobHkgY29uZmlkZW50JywgJ0NvbmZpZGVudCcpKSAlPiUgcGxvdF9hbGx1dmlhbCgpCnByaW50KHApCmdnc2F2ZSgnLi4vLi4vLi4vLi4vNV9tYW51c2NyaXB0X2ZpZ3VyZXMvRmlndXJlXzQvcmVsb2MvYWxsdXZpYWwucG5nJywgd2lkdGg9NSwgaGVpZ2h0PTUpCmdnc2F2ZSgnLi4vLi4vLi4vLi4vNV9tYW51c2NyaXB0X2ZpZ3VyZXMvRmlndXJlXzQvcmVsb2MvYWxsdXZpYWwucGRmJywgd2lkdGg9NSwgaGVpZ2h0PTUpCgpkaWZmX2xvY19hbGxfdW5pcXVlICU+JSBwbG90X2FsbHV2aWFsKCkKCmBgYAoKCgpJbiB0aGUgbmV4dCBmZXcgY2VsbHMsIHdlIHBsb3Qgc3BlY2lmaWMgc3Vic2V0cyBvZiBwcm90ZWluIHJlbG9jYWxpc2F0aW9ucyB0byBoZWxwIHdpdGggdGhlIGludGVycHJldGF0aW9uLgpgYGB7cn0KUmlibzJVbiA8LSBkaWZmX2xvY19hbGxfdW5pcXVlICU+JQogIGZpbHRlcihiYW5kbGUuYWxsb2NhdGlvbi5kbXNvLm1pbmltYWw9PSdSSUJPU09NRScsIGJhbmRsZS5hbGxvY2F0aW9uLnRnLm1pbmltYWw9PSdVbmRlZmluZWQnLCBsZXZlbCE9J0NhbmRpZGF0ZScpCgpwcmludChSaWJvMlVuKQoKcGxvdF9mb2lzKFJpYm8yVW4kcHJvdGVpbiwKICAgICAgICAgIGZvaV9uYW1lPSdSaWJvc29tZSAtPiBVbmRlZmluZWQnLAogICAgICAgICAgbW9pPWMoJ1JJQk9TT01FJywgJ0VSJywgJ1BST1RFSU4gQ09NUExFWCcsICdDWVRPU09MJyksCiAgICAgICAgICBvYmo9Y29tYmluZWRfcHJvdGVpbl9yZXNfaW5jX2JhbmRsZV9sb2MsCiAgICAgICAgICBmZWF0dXJlX2NvbD0nYmFuZGxlX2FsbG9jJywKICAgICAgICAgIHBsb3RfdHNuZT1UUlVFLAogICAgICAgICAgdW5rbm93bl9kZXNjPSdVbmRlZmluZWQnKQoKZm9yKHggaW4gUmlibzJVbiRwcm90ZWluKXsKICBwbG90X2ZvaXMoeCwKICAgICAgICAgIGZvaV9uYW1lPXgsCiAgICAgICAgICBtb2k9YygnUklCT1NPTUUnLCAnRVInLCAnUFJPVEVJTiBDT01QTEVYJywgJ05VQ0xFVVMnKSwKICAgICAgICAgIG9iaj1jb21iaW5lZF9wcm90ZWluX3Jlc19pbmNfYmFuZGxlX2xvYywKICAgICAgICAgIGZlYXR1cmVfY29sPSdiYW5kbGVfYWxsb2MnLAogICAgICAgICAgdW5rbm93bl9kZXNjPSdVbmRlZmluZWQnKQp9CmBgYAoKCmBgYHtyfQoKUmlibzJBbnkgPC0gZGlmZl9sb2NfYWxsX3VuaXF1ZSAlPiUKICBmaWx0ZXIoYmFuZGxlLmFsbG9jYXRpb24uZG1zby5taW5pbWFsPT0nUklCT1NPTUUnKQoKcHJpbnQoUmlibzJBbnkgJT4lIGFycmFuZ2UobGV2ZWwpKQoKcGxvdF9mb2lzKFJpYm8yQW55JHByb3RlaW4sCiAgICAgICAgICBmb2lfbmFtZT0nQXdheSBmcm9tIHJpYm9zb21lJywKICAgICAgICAgIG1vaT1jKCdSSUJPU09NRScsICdFUicsICdQUk9URUlOIENPTVBMRVgnLCAnQ1lUT1NPTCcpLAogICAgICAgICAgb2JqPWNvbWJpbmVkX3Byb3RlaW5fcmVzX2luY19iYW5kbGVfbG9jLAogICAgICAgICAgZmVhdHVyZV9jb2w9J2JhbmRsZV9hbGxvYycsCiAgICAgICAgICBwbG90X3RzbmU9VFJVRSwKICAgICAgICAgIHVua25vd25fZGVzYz0nVW5kZWZpbmVkJykKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KZm9yKHggaW4gUmlibzJBbnkkcHJvdGVpbil7CiAgcGxvdF9mb2lzKHgsCiAgICAgICAgICBmb2lfbmFtZT14LAogICAgICAgICAgbW9pPWMoJ1JJQk9TT01FJywgJ0VSJywgJ1BST1RFSU4gQ09NUExFWCcsICdOVUNMRVVTJyksCiAgICAgICAgICBvYmo9Y29tYmluZWRfcHJvdGVpbl9yZXMsCiAgICAgICAgICBmZWF0dXJlX2NvbD0nbWFya2VycycpCn0KYGBgCgoKCmBgYHtyfQpQTTJFUiA8LSBkaWZmX2xvY19hbGxfdW5pcXVlICU+JQogIGZpbHRlcihiYW5kbGUuYWxsb2NhdGlvbi5kbXNvLm1pbmltYWw9PSdQTScsCiAgICAgICAgIGJhbmRsZS5hbGxvY2F0aW9uLnRnLm1pbmltYWw9PSdFUicpCgpwcmludChQTTJFUikKCnBsb3RfZm9pcyhQTTJFUiRwcm90ZWluLAogICAgICAgICAgZm9pX25hbWU9J1BNLT5FUicsCiAgICAgICAgICBtb2k9YygnUE0nLCAnRVInLCAnTFlTT1NPTUUnLCAnTUlUT0NIT05EUklBJywgJ1BFUk9YSVNPTUUnKSwKICAgICAgICAgIG9iaj1jb21iaW5lZF9wcm90ZWluX3Jlc19pbmNfYmFuZGxlX2xvYywKICAgICAgICAgIGZlYXR1cmVfY29sPSdiYW5kbGVfYWxsb2MnLAogICAgICAgICAgcGxvdF90c25lPVRSVUUsCiAgICAgICAgICB1bmtub3duX2Rlc2M9J1VuZGVmaW5lZCcpCgoKYGBgCgpgYGB7cn0KQW55Mk51YyA8LSBkaWZmX2xvY19hbGxfdW5pcXVlICU+JQogIGZpbHRlcihncmVwbCgnTlVDJywgYmFuZGxlLmFsbG9jYXRpb24udGcubWluaW1hbCkpCgpwcmludChBbnkyTnVjICU+JSBhcnJhbmdlKGxldmVsKSkKCgpwbG90X2ZvaXMoQW55Mk51YyRwcm90ZWluLCBmb2lfbmFtZT0nVG8gTnVjbGVvcGxhc20nLAogICAgICAgICAgbW9pPWMoJ0NZVE9TT0wnLCAnTlVDTEVVUycsICdOVUNMRU9QTEFTTS0xJywgJ05VQ0xFT1BMQVNNLTInLCAnRVInKSwKICAgICAgICAgIG9iaj1jb21iaW5lZF9wcm90ZWluX3JlcywKICAgICAgICAgIGZlYXR1cmVfY29sPSdtYXJrZXJzJykKCmZvcih4IGluIEFueTJOdWMkcHJvdGVpbil7CiAgcGxvdF9mb2lzKHgsIGZvaV9uYW1lPXgsCiAgICAgICAgICBtb2k9YygnQ1lUT1NPTCcsICdOVUNMRVVTJywgJ05VQ0xFT1BMQVNNLTEnLCAnTlVDTEVPUExBU00tMicsICdFUicpLAogICAgICAgICAgb2JqPWNvbWJpbmVkX3Byb3RlaW5fcmVzLAogICAgICAgICAgZmVhdHVyZV9jb2w9J21hcmtlcnMnKQp9CgpgYGAKCmBgYHtyfQpnYTJhbnkgPC0gZGlmZl9sb2NfY29uZiAlPiUKICBmaWx0ZXIoYmFuZGxlLmFsbG9jYXRpb24uZG1zby5taW5pbWFsPT0nR09MR0knKQoKcHJpbnQoZ2EyYW55ICU+JSBhcnJhbmdlKGxldmVsKSkKCnBsb3RfZm9pcyhnYTJhbnkkcHJvdGVpbiwKICAgICAgICAgIGZvaV9uYW1lPSdBd2F5IGZyb20gZ29sZ2knLAogICAgICAgICAgbW9pPWMoJ0dPTEdJJywgJ0VSJywgJ0xZU09TT01FJywgJ0NZVE9TT0wnLCAnTlVDTEVVUycpLAogICAgICAgICAgb2JqPWNvbWJpbmVkX3Byb3RlaW5fcmVzLAogICAgICAgICAgZmVhdHVyZV9jb2w9J21hcmtlcnMnLAogICAgICAgICAgcGxvdF90c25lPVRSVUUpCmBgYApgYGB7cn0KcGxvdF9mb2lzKCdROTI2ODgnLAogICAgICAgICAgZm9pX25hbWU9J1E5MjY4OCcsCiAgICAgICAgICBtb2k9YygnR09MR0knLCAnRVInLCAnTFlTT1NPTUUnLCAnQ1lUT1NPTCcsICdOVUNMRVVTJyksCiAgICAgICAgICBvYmo9Y29tYmluZWRfcHJvdGVpbl9yZXNfaW5jX2JhbmRsZV9sb2MsCiAgICAgICAgICBmZWF0dXJlX2NvbD0nYmFuZGxlX2FsbG9jJywKICAgICAgICAgIHBsb3RfdHNuZT1UUlVFLAogICAgICAgICAgdW5rbm93bl9kZXNjPSdVbmRlZmluZWQnKQoKCmBgYAoKYGBge3J9CnVuMnVuIDwtIGRpZmZfbG9jX2FsbF91bmlxdWUgJT4lCiAgZmlsdGVyKGJhbmRsZS5hbGxvY2F0aW9uLmRtc28ubWluaW1hbD09J1VuZGVmaW5lZCcsCiAgICAgICAgIGJhbmRsZS5hbGxvY2F0aW9uLnRnLm1pbmltYWw9PSdVbmRlZmluZWQnLAogICAgICAgICBkaWZmLmxvYy5yZXBzPT0nMSwyLDMnKQoKcHJpbnQodW4ydW4pCgpwbG90X2ZvaXModW4ydW4kcHJvdGVpbiwKICAgICAgICAgIGZvaV9uYW1lPSc/LT4/JywKICAgICAgICAgIG1vaT1jKCdHT0xHSScsICdFUicsICdMWVNPU09NRScsICdDWVRPU09MJywgJ05VQ0xFVVMnKSwKICAgICAgICAgIG9iaj1jb21iaW5lZF9wcm90ZWluX3JlcywKICAgICAgICAgIGZlYXR1cmVfY29sPSdtYXJrZXJzJywKICAgICAgICAgIHBsb3RfdHNuZT1UUlVFKQpgYGAKCgoK